package com.utoronto.syncgallery.async_task;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;

import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Environment;
import android.util.Log;

import com.dropbox.client2.DropboxAPI;
import com.dropbox.client2.DropboxAPI.Entry;
import com.dropbox.client2.exception.DropboxException;
import com.dropbox.client2.exception.DropboxServerException;
import com.utoronto.syncgallery.GalleryActivity;
import com.utoronto.syncgallery.R;
import com.utoronto.syncgallery.utils.DropboxAuthSession;
import com.utoronto.syncgallery.utils.SyncGalleryConstants;

public class DropboxSyncTask extends AsyncTask<Void, Void, Void> {

	Entry DropboxEntry;
	Entry rootEntries;
	File localstorage;
	File dirstorage;
	File[] LocalPictureList;
	ArrayList<String> LocalPictureArray = new ArrayList<String>();
	ArrayList<String> LocalDirArray = new ArrayList<String>();

	Context mContext;
	NotificationManager mNotificationManager;
	Notification notification;
	GalleryActivity mGalleryActivity;

	public DropboxSyncTask(Context context, NotificationManager nm,
			GalleryActivity ga) {
		mGalleryActivity = ga;
		mContext = context;
		mNotificationManager = nm;
	}

	@Override
	protected void onPreExecute() {
		/** Sets up the message to be displayed in notification bar */
		CharSequence text = "SyncGallery+";
		notification = new Notification(R.drawable.ic_launcher, text,
				System.currentTimeMillis());
		notification.icon = R.drawable.small_logo;
		notification.flags = Notification.FLAG_AUTO_CANCEL; // clicking
															// notification will
															// not do anything
		notification.setLatestEventInfo(mContext, "SyncGallery Notification",
				"Syncing...",
				PendingIntent.getActivity(mContext, 0, new Intent(), 0));
		mNotificationManager.notify(1, notification);
	}

	@Override
	protected Void doInBackground(Void... params) {
		/** Start by looking in root directory */
		directory("/");
		return null;
	}

	protected Void directory(String dir) {
		/** Recursive function that will be update files at every directory */
		List<DropboxAPI.Entry> DropboxPictureList = null;
		localstorage = Environment
				.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES
						+ "/SyncGallery");
		dirstorage = Environment
				.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES
						+ "/SyncGallery" + dir);

		LocalPictureList = dirstorage.listFiles();

		check(mContext.fileList());
		try {
			rootEntries = DropboxAuthSession.getInstance().getApi()
					.metadata(dir, 0, null, true, null);
			DropboxPictureList = rootEntries.contents;
		} catch (DropboxServerException e) {
			if (e.error == DropboxServerException._404_NOT_FOUND) {
				try {
					DropboxAuthSession.getInstance().getApi().createFolder(dir);
				} catch (DropboxException e1) {
					e1.printStackTrace();
				}
			}
		} catch (DropboxException e) {
			e.printStackTrace();
			rootEntries = null;
			// Server busy
			return null;
		}

		// If we have an empty directory, we create it on dropbox
		if (LocalPictureList == null) {
			try {
				DropboxAuthSession.getInstance().getApi().createFolder(dir);
			} catch (DropboxException e1) {
				e1.printStackTrace();
			}
			LocalPictureList = new File[0];
		}
		// Loop through all the files on current directory, and updates each if
		// necessary
		for (File f : LocalPictureList) {
			if (!f.isHidden()) {
				if (f.isDirectory()) {
					String dirname = "/"
							+ f.getAbsolutePath().split(
									Environment.DIRECTORY_PICTURES
											+ "/SyncGallery")[1];
					if (dirname.charAt(1) == '/')
						dirname = dirname.substring(1);
					directory(dirname);
					LocalDirArray.add(dirname);
				} else {
					String filename = f.getAbsolutePath().split(
							Environment.DIRECTORY_PICTURES + "/SyncGallery")[1];
					LocalPictureArray.add(filename);
					try {
						DropboxEntry = DropboxAuthSession.getInstance()
								.getApi()
								.metadata(filename, 0, null, false, null);
						// Meta data for local file
						FileInputStream meta = mContext.openFileInput(filename
								.substring(1).replace("/", "-") + ".meta");
						// Meta data for the most recent file
						FileInputStream newest = mContext
								.openFileInput(filename.substring(1).replace(
										"/", "-")
										+ ".newest");
						// Meta data of file on Dropbox
						String revValue = DropboxEntry.rev;

						BufferedReader br = new BufferedReader(
								new InputStreamReader(meta));
						String metaValue = br.readLine();
						br = new BufferedReader(new InputStreamReader(newest));
						String newestValue = br.readLine();
						meta.close();
						newest.close();

						// if file on dropbox is newer
						if ((metaValue.equals(newestValue))
								&& !(revValue.equals(newestValue))) {
							if (DropboxEntry.isDeleted) {
								f.delete();
								mContext.deleteFile(filename.substring(1)
										.replace("/", "-") + ".meta");
								mContext.deleteFile(filename.substring(1)
										.replace("/", "-") + ".newest");
							} else {
								download(DropboxEntry);
								Log.i("DOWLOAD", "Downloaded "
										+ DropboxEntry.path);
							}
							// if file on phone is newer
						} else if (!(metaValue.equals(newestValue))
								&& (revValue.equals(newestValue))) {
							upload(f);
							Log.i("UPLOAD", "Uploaded " + f.getPath());
						}

					} catch (DropboxServerException e1) {
						// Tried to get meta data for a file that does not exist
						// on dropbox
						// that means it is a new file, so we upload
						if (e1.error == DropboxServerException._404_NOT_FOUND) {
							upload(f);
							Log.i("UPLOAD", "Uploaded " + f.getPath());
						}
					} catch (DropboxException e1) {
						e1.printStackTrace();
					} catch (FileNotFoundException e) {
						// Cannot open meta data for file
						// If the file is not deleted on dropbox we download
						if (!(DropboxEntry.isDeleted)) {
							download(DropboxEntry);
							Log.i("DOWLOAD", "Downloaded " + DropboxEntry.path);
						} else {
							upload(f);
							Log.i("UPLOAD", "Uploaded " + f.getPath());
						}
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
			}
		}
		// Loops through files in Dropbox and updates if necessary
		for (Entry e : DropboxPictureList) {
			String filename2 = e.path;
			if (!e.isDir) {
				if ((!LocalPictureArray.contains(filename2)) && (!e.isDeleted)) {
					download(e);
				}
			} else {
				if (!LocalDirArray.contains(filename2)) {
					File newDir = new File(
							SyncGalleryConstants.Gallery_SYNC_DIR + filename2);
					newDir.mkdirs();
					directory(filename2);
				}
			}
		}
		return null;

	}

	@Override
	protected void onPostExecute(Void result) {
		// Set the new notification
		notification.setLatestEventInfo(mContext, "SyncGallery Notification",
				"Synced",
				PendingIntent.getActivity(mContext, 0, new Intent(), 0));
		mNotificationManager.notify(1, notification);
		// Tells the device to search for new images
		mContext.sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri
				.parse("file://" + Environment.getExternalStorageDirectory())));
		if (mGalleryActivity.getmCurrentDir().getAbsolutePath()
				.equals(SyncGalleryConstants.Gallery_SYNC_DIR))
			mGalleryActivity.browseTo(mGalleryActivity.getmCurrentDir());
	}

	protected void check(String[] files) {
		/**
		 * Checks the .meta and .newest files. If there is a .newest but not a
		 * .meta file, that means that file has been deleted on local device so
		 * we need to delete from dropbox.
		 */

		for (String filename : files) {
			String newFilename = filename.split(".meta|.newest")[0];
			try {
				FileInputStream meta = mContext.openFileInput(newFilename
						+ ".meta");
			} catch (FileNotFoundException e) {
				try {
					DropboxAuthSession.getInstance().getApi()
							.delete("/" + newFilename.replace("-", "/"));
					mContext.deleteFile(filename);
				} catch (DropboxException e1) {
					e1.printStackTrace();
				}
			}

		}
	}

	protected void download(Entry e) {
		/** Downloads entry from dropbox */

		File file = new File(localstorage, e.path);
		OutputStream os;
		try {
			os = new FileOutputStream(file);
			DropboxAuthSession.getInstance().getApi()
					.getFile(e.path, null, os, null);
			os.close();

			// Update new .meta and .newest files
			FileOutputStream fos = mContext.openFileOutput(e.path.substring(1)
					.replace("/", "-") + ".meta", Context.MODE_PRIVATE);
			fos.write(e.rev.getBytes());
			fos.close();
			FileOutputStream fos2 = mContext.openFileOutput(e.path.substring(1)
					.replace("/", "-") + ".newest", Context.MODE_PRIVATE);
			fos2.write(e.rev.getBytes());
			fos2.close();

			// Refresh thumbnail
			File thumbfile = new File(file.getParentFile().getAbsolutePath()
					+ SyncGalleryConstants.Gallery_THUMBNAILS + file.getName());
			thumbfile.delete();

		} catch (FileNotFoundException e1) {
			e1.printStackTrace();
		} catch (DropboxException e1) {
			e1.printStackTrace();
		} catch (IOException e1) {
			e1.printStackTrace();
		}

	}

	protected void upload(File f) {
		/** Upload file to dropbox */

		String pathname = f.getAbsolutePath().split(
				Environment.DIRECTORY_PICTURES + "/SyncGallery")[1];
		try {
			FileInputStream file = new FileInputStream(f);
			Entry fileinfo = DropboxAuthSession.getInstance().getApi()
					.putFile(pathname, file, file.available(), null, null);

			// Update/Create .meta .newest files
			FileOutputStream fos = mContext.openFileOutput(pathname
					.substring(1).replace("/", "-") + ".meta",
					Context.MODE_PRIVATE);
			fos.write(fileinfo.rev.getBytes());
			fos.close();
			FileOutputStream fos2 = mContext.openFileOutput(
					pathname.substring(1).replace("/", "-") + ".newest",
					Context.MODE_PRIVATE);
			fos2.write(fileinfo.rev.getBytes());
			fos2.close();

		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (DropboxException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

}
